/**
 * upload
 * 上传组件
 */

layui.define(['lay', 'layer'], function (exports) {
  "use strict";

  var $ = layui.$
      , layer = layui.layer
      , device = layui.device()

      //外部接口
      , upload = {
        config: {} //全局配置项

        //设置全局项
        , set: function (options) {
          var that = this;
          that.config = $.extend({}, that.config, options);
          return that;
        }

        //事件
        , on: function (events, callback) {
          return layui.onevent.call(this, MOD_NAME, events, callback);
        }
      }

      //操作当前实例
      , thisUpload = function () {
        var that = this;
        return {
          upload: function (files) {
            that.upload.call(that, files);
          }
          , reload: function (options) {
            that.reload.call(that, options);
          }
          , config: that.config
        }
      }

      //字符常量
      , MOD_NAME = 'upload', ELEM = 'layui-upload', THIS = 'layui-this',
      SHOW = 'layui-show', HIDE = 'layui-hide', DISABLED = 'layui-disabled'

      , ELEM_FILE = 'layui-upload-file', ELEM_FORM = 'layui-upload-form',
      ELEM_IFRAME = 'layui-upload-iframe', ELEM_CHOOSE = 'layui-upload-choose',
      ELEM_DRAG = 'layui-upload-drag'


      //构造器
      , Class = function (options) {
        var that = this;
        that.config = $.extend({}, that.config, upload.config, options);
        that.render();
      };

  //默认配置
  Class.prototype.config = {
    accept: 'images' //允许上传的文件类型：images/file/video/audio
    , exts: '' //允许上传的文件后缀名
    , auto: true //是否选完文件后自动上传
    , bindAction: '' //手动上传触发的元素
    , url: '' //上传地址
    , force: '' //强制规定返回的数据格式，目前只支持是否强制 json
    , field: 'file' //文件字段名
    , acceptMime: '' //筛选出的文件类型，默认为所有文件
    , method: 'post' //请求上传的 http 类型
    , data: {} //请求上传的额外参数
    , drag: true //是否允许拖拽上传
    , size: 0 //文件限制大小，默认不限制
    , number: 0 //允许同时上传的文件数，默认不限制
    , multiple: false //是否允许多文件上传，不支持ie8-9
  };

  //初始渲染
  Class.prototype.render = function (options) {
    var that = this
        , options = that.config;

    options.elem = $(options.elem);
    options.bindAction = $(options.bindAction);

    that.file();
    that.events();
  };

  //追加文件域
  Class.prototype.file = function () {
    var that = this
        , options = that.config
        , elemFile = that.elemFile = $([
      '<input class="' + ELEM_FILE + '" type="file" accept="'
      + options.acceptMime + '" name="' + options.field + '"'
      , (options.multiple ? ' multiple' : '')
      , '>'
    ].join(''))
        , next = options.elem.next();

    if (next.hasClass(ELEM_FILE) || next.hasClass(ELEM_FORM)) {
      next.remove();
    }

    //包裹ie8/9容器
    if (device.ie && device.ie < 10) {
      options.elem.wrap('<div class="layui-upload-wrap"></div>');
    }

    that.isFile() ? (
        that.elemFile = options.elem
            , options.field = options.elem[0].name
    ) : options.elem.after(elemFile);

    //初始化ie8/9的Form域
    if (device.ie && device.ie < 10) {
      that.initIE();
    }
  };

  //ie8-9初始化
  Class.prototype.initIE = function () {
    var that = this
        , options = that.config
        , iframe = $(
        '<iframe id="' + ELEM_IFRAME + '" class="' + ELEM_IFRAME + '" name="'
        + ELEM_IFRAME + '" frameborder="0"></iframe>')
        , elemForm = $(['<form target="' + ELEM_IFRAME + '" class="' + ELEM_FORM
    + '" method="post" key="set-mine" enctype="multipart/form-data" action="'
    + options.url + '">'
      , '</form>'].join(''));

    //插入iframe    
    $('#' + ELEM_IFRAME)[0] || $('body').append(iframe);

    //包裹文件域
    if (!options.elem.next().hasClass(ELEM_FORM)) {
      that.elemFile.wrap(elemForm);

      //追加额外的参数
      options.elem.next('.' + ELEM_FORM).append(function () {
        var arr = [];
        layui.each(options.data, function (key, value) {
          value = typeof value === 'function' ? value() : value;
          arr.push(
              '<input type="hidden" name="' + key + '" value="' + value + '">')
        });
        return arr.join('');
      }());
    }
  };

  //异常提示
  Class.prototype.msg = function (content) {
    return layer.msg(content, {
      icon: 2
      , shift: 6
    });
  };

  //判断绑定元素是否为文件域本身
  Class.prototype.isFile = function () {
    var elem = this.config.elem[0];
    if (!elem) {
      return;
    }
    return elem.tagName.toLocaleLowerCase() === 'input' && elem.type === 'file'
  }

  //预读图片信息
  Class.prototype.preview = function (callback) {
    var that = this;
    if (window.FileReader) {
      layui.each(that.chooseFiles, function (index, file) {
        var reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function () {
          callback && callback(index, file, this.result);
        }
      });
    }
  };

  //执行上传
  Class.prototype.upload = function (files, type) {
    var that = this
        , options = that.config
        , elemFile = that.elemFile[0]

        //高级浏览器处理方式，支持跨域
        , ajaxSend = function () {
          var successful = 0, failed = 0
              , items = files || that.files || that.chooseFiles || elemFile.files
              , allDone = function () { //多文件全部上传完毕的回调
            if (options.multiple && successful + failed === that.fileLength) {
              typeof options.allDone === 'function' && options.allDone({
                total: that.fileLength
                , successful: successful
                , failed: failed
              });
            }
          };
          layui.each(items, function (index, file) {
            var formData = new FormData();

            //追加额外的参数
            layui.each(options.data, function (key, value) {
              value = typeof value === 'function' ? value() : value;
              formData.append(key, value);
            });

            //最后添加 file 到表单域
            formData.append(options.field, file);

            //提交文件
            var opts = {
              url: options.url
              , type: 'post' //统一采用 post 上传
              , data: formData
              , contentType: false
              , processData: false
              , dataType: 'json'
              , headers: options.headers || {}
              //成功回调
              , success: function (res) {
                successful++;
                done(index, res);
                allDone();
              }
              //异常回调
              , error: function (e) {
                failed++;
                that.msg('Request URL is abnormal: ' + (e.statusText || 'error'));
                error(index);
                allDone();
              }
            };
            //进度条
            if (typeof options.progress === 'function') {
              opts.xhr = function () {
                var xhr = $.ajaxSettings.xhr();
                //上传进度
                xhr.upload.addEventListener("progress", function (obj) {
                  if (obj.lengthComputable) {
                    var percent = Math.floor((obj.loaded / obj.total) * 100); //百分比
                    options.progress(percent,
                        (options.item ? options.item[0] : options.elem[0]), obj,
                        index);
                  }
                });
                return xhr;
              }
            }
            $.ajax(opts);
          });
        }

        //低版本 IE 处理方式，不支持跨域
        , iframeSend = function () {
          var iframe = $('#' + ELEM_IFRAME);

          that.elemFile.parent().submit();

          //获取响应信息
          clearInterval(Class.timer);
          Class.timer = setInterval(function () {
            var res, iframeBody = iframe.contents().find('body');
            try {
              res = iframeBody.text();
            } catch (e) {
              that.msg('Cross-domain requests are not supported');
              clearInterval(Class.timer);
              error();
            }
            if (res) {
              clearInterval(Class.timer);
              iframeBody.html('');
              done(0, res);
            }
          }, 30);
        }

        //统一回调
        , done = function (index, res) {
          that.elemFile.next('.' + ELEM_CHOOSE).remove();
          elemFile.value = '';

          if (options.force === 'json') {
            if (typeof res !== 'object') {
              try {
                res = JSON.parse(res);
              } catch (e) {
                res = {};
                return that.msg('Please return JSON data format');
              }
            }
          }

          typeof options.done === 'function' && options.done(res, index || 0,
              function (files) {
                that.upload(files);
              });
        }

        //统一网络异常回调
        , error = function (index) {
          if (options.auto) {
            elemFile.value = '';
          }
          typeof options.error === 'function' && options.error(index || 0,
              function (files) {
                that.upload(files);
              });
        }

        , exts = options.exts
        , check, value = function () {
          var arr = [];
          layui.each(files || that.chooseFiles, function (i, item) {
            arr.push(item.name);
          });
          return arr;
        }()

        //回调返回的参数
        , args = {
          //预览
          preview: function (callback) {
            that.preview(callback);
          }
          //上传
          , upload: function (index, file) {
            var thisFile = {};
            thisFile[index] = file;
            that.upload(thisFile);
          }
          //追加文件到队列
          , pushFile: function () {
            that.files = that.files || {};
            layui.each(that.chooseFiles, function (index, item) {
              that.files[index] = item;
            });
            return that.files;
          }
          //重置文件
          , resetFile: function (index, file, filename) {
            var newFile = new File([file], filename);
            that.files = that.files || {};
            that.files[index] = newFile;
          }
        }

        //提交上传
        , send = function () {
          //上传前的回调 - 如果回调函数明确返回false，则停止上传(#pulls55)
          if (options.before && (options.before(args) === false)) {
            return;
          }

          //IE兼容处理
          if (device.ie) {
            return device.ie > 9 ? ajaxSend() : iframeSend();
          }

          ajaxSend();
        }

        //文件类型名称
        , typeName = ({
          file: '文件'
          , images: '图片'
          , video: '视频'
          , audio: '音频'
        })[options.accept] || '文件';

    //校验文件格式
    value = value.length === 0
        ? ((elemFile.value.match(/[^\/\\]+\..+/g) || []) || '')
        : value;

    if (value.length === 0) {
      return;
    }

    //根据文件类型校验
    switch (options.accept) {
      case 'file': //一般文件
        layui.each(value, function (i, item) {
          if (exts && !RegExp('.\\.(' + exts + ')$', 'i').test(escape(item))) {
            return check = true;
          }
        });
        break;
      case 'video': //视频文件
        layui.each(value, function (i, item) {
          if (!RegExp(
              '.\\.(' + (exts || 'avi|mp4|wma|rmvb|rm|flash|3gp|flv') + ')$',
              'i').test(escape(item))) {
            return check = true;
          }
        });
        break;
      case 'audio': //音频文件
        layui.each(value, function (i, item) {
          if (!RegExp('.\\.(' + (exts || 'mp3|wav|mid') + ')$', 'i').test(
              escape(item))) {
            return check = true;
          }
        });
        break;
      default: //图片文件
        layui.each(value, function (i, item) {
          if (!RegExp('.\\.(' + (exts || 'jpg|png|gif|bmp|jpeg') + ')$',
              'i').test(escape(item))) {
            return check = true;
          }
        });
        break;
    }

    //校验失败提示
    if (check) {
      that.msg('选择的' + typeName + '中包含不支持的格式');
      return elemFile.value = '';
    }

    //选择文件的回调      
    if (type === 'choose' || options.auto) {
      options.choose && options.choose(args);
      if (type === 'choose') {
        return;
      }
    }

    //检验文件数量
    that.fileLength = function () {
      var length = 0
          , items = files || that.files || that.chooseFiles || elemFile.files;
      layui.each(items, function () {
        length++;
      });
      return length;
    }();

    if (options.number && that.fileLength > options.number) {
      return that.msg(
          '同时最多只能上传: ' + options.number + ' 个文件'
          + '<br>您当前已经选择了: ' + that.fileLength + ' 个文件'
      );
    }

    //检验文件大小
    if (options.size > 0 && !(device.ie && device.ie < 10)) {
      var limitSize;

      layui.each(that.chooseFiles, function (index, file) {
        if (file.size > 1024 * options.size) {
          var size = options.size / 1024;
          size = size >= 1 ? (size.toFixed(2) + 'MB') : options.size + 'KB'
          elemFile.value = '';
          limitSize = size;
        }
      });
      if (limitSize) {
        return that.msg('文件大小不能超过 ' + limitSize);
      }
    }

    send();
  };

  //重置方法
  Class.prototype.reload = function (options) {
    options = options || {};
    delete options.elem;
    delete options.bindAction;

    var that = this
        , options = that.config = $.extend({}, that.config, upload.config,
        options)
        , next = options.elem.next();

    //更新文件域相关属性
    next.attr({
      name: options.name
      , accept: options.acceptMime
      , multiple: options.multiple
    });
  };

  //事件处理
  Class.prototype.events = function () {
    var that = this;
    var options = that.config;

    // 设置当前选择的文件队列
    var setChooseFile = function (files) {
      that.chooseFiles = {};
      layui.each(files, function (i, item) {
        var time = new Date().getTime();
        that.chooseFiles[time + '-' + i] = item;
      });
    };

    // 设置选择的文本
    var setChooseText = function (files, filename) {
      var elemFile = that.elemFile
          , item = options.item ? options.item : options.elem
          , value = files.length > 1
          ? files.length + '个文件'
          : ((files[0] || {}).name || (elemFile[0].value.match(/[^\/\\]+\..+/g)
              || []) || '');

      if (elemFile.next().hasClass(ELEM_CHOOSE)) {
        elemFile.next().remove();
      }
      that.upload(null, 'choose');
      if (that.isFile() || options.choose) {
        return;
      }
      elemFile.after('<span class="layui-inline ' + ELEM_CHOOSE + '">' + value
          + '</span>');
    };

    // 合并 lay-options/lay-data 属性配置项
    var extendAttrs = function () {
      var othis = $(this);
      var data = othis.attr('lay-data') || othis.attr('lay-options'); // 优先兼容旧版本

      if (data) {
        that.config = $.extend({}, options, lay.options(this, {
          attr: othis.attr('lay-data') ? 'lay-data' : null
        }));
      }
    };

    //点击上传容器
    options.elem.off('upload.start').on('upload.start', function () {
      var othis = $(this);

      extendAttrs.call(this);
      that.config.item = othis;
      that.elemFile[0].click();
    });

    //拖拽上传
    if (!(device.ie && device.ie < 10)) {
      options.elem.off('upload.over').on('upload.over', function () {
        var othis = $(this)
        othis.attr('lay-over', '');
      })
      .off('upload.leave').on('upload.leave', function () {
        var othis = $(this)
        othis.removeAttr('lay-over');
      })
      .off('upload.drop').on('upload.drop', function (e, param) {
        var othis = $(this);
        var files = param.originalEvent.dataTransfer.files || [];

        othis.removeAttr('lay-over');
        extendAttrs.call(this);
        setChooseFile(files);

        options.auto ? that.upload() : setChooseText(files); //是否自动触发上传
      });
    }

    //文件选择
    that.elemFile.off('upload.change').on('upload.change', function () {
      var files = this.files || [];

      extendAttrs.call(this);
      setChooseFile(files);
      options.auto ? that.upload() : setChooseText(files); //是否自动触发上传
    });

    //手动触发上传
    options.bindAction.off('upload.action').on('upload.action', function () {
      that.upload();
    });

    //防止事件重复绑定
    if (options.elem.data('haveEvents')) {
      return;
    }

    that.elemFile.on('change', function () {
      $(this).trigger('upload.change');
    });

    options.elem.on('click', function () {
      if (that.isFile()) {
        return;
      }
      $(this).trigger('upload.start');
    });

    if (options.drag) {
      options.elem.on('dragover', function (e) {
        e.preventDefault();
        $(this).trigger('upload.over');
      }).on('dragleave', function (e) {
        $(this).trigger('upload.leave');
      }).on('drop', function (e) {
        e.preventDefault();
        $(this).trigger('upload.drop', e);
      });
    }

    options.bindAction.on('click', function () {
      $(this).trigger('upload.action');
    });

    options.elem.data('haveEvents', true);
  };

  //核心入口  
  upload.render = function (options) {
    var inst = new Class(options);
    return thisUpload.call(inst);
  };

  exports(MOD_NAME, upload);
});

